using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace VCity
{
    [ExecuteInEditMode]
    public class SkyManager : MonoBehaviour
    {
        public static readonly int ID_Exposure = Shader.PropertyToID("_Exposure");
        public static readonly int ID_Rotation = Shader.PropertyToID("_Rotation");
        public static LocalKeyword ID_Emission;

        [Header("Sun")]
        public Light sun;
        public Vector2 cloudSpeed;
        UniversalAdditionalLightData sunData;

        [Header("Skybox")]
        public Material daytimeSkyMaterial;
        public Material nightSkyMaterial;
        float startExposure;
        float startRotation;

        [Header("Materials")]
        public List<Material> emissionMats;

        [Header("Config")]
        [Range(0, 5)]
        public float dayIntensity = 1.5f;
        [Range(0, 1)]
        public float dayExposure = 0.5f;

        [Header("Runtime")]
        [Range(0.01f, 1)]
        public float daytime;
        bool isNight;

        Dictionary<Shader, LocalKeyword> emissionKeyDic;

        void Awake()
        {
            sunData = sun.GetComponent<UniversalAdditionalLightData>();

            startExposure = daytimeSkyMaterial.GetFloat(ID_Exposure);
            startRotation = daytimeSkyMaterial.GetFloat(ID_Rotation);
            sunData.lightCookieOffset = Vector2.zero;
        }

        private void OnEnable()
        {
            ID_Emission = new LocalKeyword(Shader.Find("Universal Render Pipeline/Lit"), "_EMISSION");
            isNight = IsNight(daytime);

            emissionKeyDic = new Dictionary<Shader, LocalKeyword>();
            for (int i = 0, length = emissionMats.Count; i < length; i++)
            {
                var shader = emissionMats[i].shader;
                if (!emissionKeyDic.ContainsKey(shader))
                    emissionKeyDic[shader] = new LocalKeyword(shader, "_EMISSION");
            }
        }

        private void OnDestroy()
        {
            daytimeSkyMaterial.SetFloat(ID_Exposure, dayExposure);
            daytimeSkyMaterial.SetFloat(ID_Rotation, startRotation);
            sunData.lightCookieOffset = Vector2.zero;
        }

        void Update()
        {
            Vector2 cloudSpeedMul = cloudSpeed;
#if UNITY_EDITOR
            cloudSpeedMul = (UnityEditor.EditorApplication.isPlaying ? 0 : 1) * cloudSpeed;
#endif
            sunData.lightCookieOffset += Time.deltaTime * cloudSpeedMul;

            // Day time
            float exposure = Mathf.Lerp(0, dayExposure, daytime);
            float sunIntensity = Mathf.Lerp(0, dayIntensity, daytime);
            bool isNight2 = IsNight(daytime);
            bool dayChanged = isNight != isNight2;
            isNight = isNight2;

            // skybox, sun
            if (!isNight)
                daytimeSkyMaterial.SetFloat(ID_Exposure, exposure);
            sun.intensity = sunIntensity;

            if (dayChanged)
            {
                // 
                RenderSettings.skybox = isNight ? nightSkyMaterial : daytimeSkyMaterial;

                // emission
                for (int i = 0, length = emissionMats.Count; i < length; i++)
                {
                    var m = emissionMats[i];
                    if (emissionKeyDic.TryGetValue(m.shader, out var key))
                    {
                        m.SetKeyword(key, isNight2);
                    }
                }
            }
        }

        public static bool IsNight(float daytime)
        {
            return daytime < 0.1f;
        }
    }
}